package AIEngine;

import java.util.LinkedList;
import java.util.ListIterator;

public class MinMaxEngine implements AIEngine{

	private int maxDepth;
	private int limitMinMax;		// represents the limit beyond which use minmax
    
    /**
     * Constructor for the minmaxengine by commiting the playerside
     *
     * @see NodeComparator
     * @param black true = firstplayer, false = secondplayer.
     */
    public MinMaxEngine(boolean black,int iaLevel) {
        maxDepth = iaLevel;
        limitMinMax = 4;
    }
    
    
    /**
     * Checks if the search is supposed to cancel.
     *
     * @param node current position
     * @return <b>true</b> canceling search, <b>false</b> going on search
     */
    public boolean cutOff(Node node) {

    	if(node.getTurnsLeft() <= limitMinMax)
    		maxDepth = limitMinMax;
    		
        if (node.getDepth() >= maxDepth) {
            return true;
        }

        return false;
    }
    
	
    /**
     * getNodes detects all sequence positions for a node
     *
     * @see Node
     * @param node root node, hear starts the search
     * @return childslist a linkedlist filled with all sequence positions for the root node
     */
    public LinkedList getNodes(Node node) {
        LinkedList<Node> childrenList = new LinkedList();
    	int[][] workplace = new int[6][6];
    	int[][] tempBoard = new int[6][6];
    	int marble = 0;
    	
    	if(node.getCamp()){
    		marble = -1;
    	}
    	else{
    		marble = 1;
    	}
    	
        if (BasicAI.checkWin(node.getBoard(), true) == true) {
            return null;
        }
        if (BasicAI.checkWin(node.getBoard(), false) == true) {
            return null;
        }

        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 6; j++) {
                if (BasicAI.isEmpty(node.getBoard()[i][j])) {
                	
                	BasicAI.fill(node.getBoard(), tempBoard);
                	tempBoard[i][j] = marble;
                	
                	workplace = BasicAI.rotate(tempBoard, 1, 1);
                    childrenList.add(new Node(node, new int[]{i, j, 1, 1}, workplace, node.getTurnsLeft()));

                    workplace = BasicAI.rotate(tempBoard, 1, -1);
                    childrenList.add(new Node(node, new int[]{i, j, 1, -1}, workplace, node.getTurnsLeft()));

                    workplace = BasicAI.rotate(tempBoard, 2, 1);
                    childrenList.add(new Node(node, new int[]{i, j, 2, 1}, workplace, node.getTurnsLeft()));

                    workplace = BasicAI.rotate(tempBoard, 2, -1);
                    childrenList.add(new Node(node, new int[]{i, j, 2, -1}, workplace, node.getTurnsLeft()));
                    
                    workplace = BasicAI.rotate(tempBoard, 3, 1);
                    childrenList.add(new Node(node, new int[]{i, j, 3, 1}, workplace, node.getTurnsLeft()));
                    
                    workplace = BasicAI.rotate(tempBoard, 3, -1);
                    childrenList.add(new Node(node, new int[]{i, j, 3, -1}, workplace, node.getTurnsLeft()));
                    
                    workplace = BasicAI.rotate(tempBoard, 4, 1);
                    childrenList.add(new Node(node, new int[]{i, j, 4, 1}, workplace, node.getTurnsLeft()));
                    
                    workplace = BasicAI.rotate(tempBoard, 4, -1);
                    childrenList.add(new Node(node, new int[]{i, j, 4, -1}, workplace, node.getTurnsLeft()));
                   
                }
            }
        }
        
        if(childrenList.isEmpty())
        	return null;

        return childrenList;
    }
    
    
    /**
     * Takes a node and evaluates the given position from it.
     * 
     * BasicAI.checkWin
     * @param node
     * @return value
     */
	public long evalNode(Node node) {

		long tmpValue = 0;

		tmpValue = BasicAI.fullJudge(node.getBoard(), node.getCamp(), node.getDepth(), node.getTurnsLeft(), limitMinMax);
		node.setValue(tmpValue);

		return tmpValue;
	}
    
    
    /**
     * Setter for the depth
     * @param depth new value of depth
     */
    public void setDepth(int depth) {
        this.maxDepth = depth;
    }

    /**
     * Emits the best next possible move, depending on the root node
     *
     * @see Node
     * @param node root for the calculation of the worst following move
     * @return next best move
     */
    public long maxNode(Node node) {
    	long v = Long.MIN_VALUE;
        LinkedList children;
        ListIterator iterator;
        Node tmpNode;
        long  value;
      
        if (cutOff(node)) {
            return evalNode(node);
        }
        
        children = getNodes(node);

        if (children == null) {
            return evalNode(node);
        }

        iterator = children.listIterator(0);
        while (iterator.hasNext()) {
            tmpNode = (Node) iterator.next();
            
            value =  minNode(tmpNode);
            
            if(value > v)
            	v = value;
        }
        node.setDepth(node.getDepth()+1);

        return v;
    }

    /**
     * Emits the worst next possible move, depending on the root node
     *
     * @see Node
     * @param node root for the calculation of the worst following move
     * @return worst next possible move
     */
    public long minNode(Node node) {
    	long v = Long.MAX_VALUE;
        LinkedList children;
        ListIterator iterator;
        Node tmpNode;
        long value;


        if (cutOff(node)) {
            return evalNode(node);
        }
        
        children = getNodes(node);

        if (children == null) {
            return evalNode(node);
        }

        iterator = children.listIterator(0);
        while (iterator.hasNext()) {
            tmpNode = (Node) iterator.next();
            
            value =  maxNode(tmpNode);
            if(value < v)
            	v = value;
        }
		node.setDepth(node.getDepth()+1);

        return v;

    }


    /**
     * Searchs with the alpha-beta pruning a preferably and maybe best move.
     * Depending on the searchdepth and the timelimit
     *
     * @param node basic position for the search
     * @return Node, the best move is returned as a node and <b>null</b> will
     * be returned if ther is no next move or we don't findet it
     */
	public Node getBestMove(Node node) {
		LinkedList children;
		ListIterator iterator;
		Node tmpNode;
		Node bestNode = null;
		long v = Long.MIN_VALUE;
		
		children = getNodes(node);
		if (children == null) {
			return null;
		}

		iterator = children.listIterator(0);
		while (iterator.hasNext()) {
			tmpNode = (Node) iterator.next();

			tmpNode.setValue(minNode(tmpNode));

			if (tmpNode.getValue() >= v) {
				if (tmpNode.getValue() > v) {
					v = tmpNode.getValue();
					bestNode = tmpNode;
				}
				if (tmpNode.getValue() == v && tmpNode.getDepth() < bestNode.getDepth()) {
					v = tmpNode.getValue();
					bestNode = tmpNode;
				}
			}
		}

		bestNode.setDepth(0);

		return bestNode;

	}

}
